GH = window.GH || {};

GH.publics = {
   canvas: null,
   ctx: null,
   img: null
};

GH.util = window.GH.util || {};


/******************************************************************************
 * INIT
 ******************************************************************************/
GH.init = function(pScreenId)
{
   var canvasId = 'gh_el_canvas';
   
   $('#' + pScreenId).html('<canvas id="' + canvasId + '"></canvas>');
   
   GH.publics.canvas = ($('#' + canvasId))[0];
   GH.publics.ctx = GH.publics.canvas.getContext('2d');
   
   GH.publics.img = new Image();
   GH.publics.img.src = '../public/img/pic10.jpg';
   GH.publics.img.onload = GH.loadImg;
};


/******************************************************************************
 * LOAD IMG
 * Loads an image into the canvas context
 ******************************************************************************/
GH.loadImg = function()
{
   GH.publics.canvas.width = 800;
   GH.publics.canvas.height = 500;
   GH.publics.ctx.drawImage(GH.publics.img, 0, 0, GH.publics.img.width, GH.publics.img.height);
};


/******************************************************************************
 * IMG RESET
 * Reloads current image assigned to be displayed in default canvas context
 ******************************************************************************/
GH.imgReset = function()
{
   GH.publics.ctx.clearRect(0, 0, GH.publics.canvas.width, GH.publics.canvas.height);
   GH.loadImg();
};


/******************************************************************************
 * IMG GRAY
 * Grayscales the pixel data in the global canvas context
 ******************************************************************************/
GH.imgGray = function()
{
   var pxData = GH.publics.ctx.getImageData(0, 0, 
      GH.publics.canvas.width, 
      GH.publics.canvas.height
   );
      
   var len = pxData.data.length;

   for(var i = 0; i < len; i+= 4)
   {
      var gray = pxData.data[i] * 0.21 + pxData.data[i + 1] * 0.71 + pxData.data[i + 2] * 0.07;

      pxData.data[i] = gray;
      pxData.data[i + 1] = gray;
      pxData.data[i + 2] = gray;
   }

   GH.publics.ctx.putImageData(pxData, 0, 0);
};


/******************************************************************************
 * IMG OTSU
 * Uses Otsu's thresholding method to create a binary image out of the 
 * pixel data in the global canvas context
 ******************************************************************************/
GH.imgOtsu = function()
{
   alert('otsu');
};


/******************************************************************************
 * IMG HISTO
 * Creates a histogram out of the pixel data in the default canvas context
 ******************************************************************************/
GH.imgHisto = function(pPoints, pBuckets, pMinVal, pMaxVal)
{
   var dataSaved = 0;
   
   var isFinished = function(pTotalSaved)
   {
      if(pTotalSaved == 3)
         alert('Data saved successfully.');
   };
   
   
   // SAVE RED CHANNEL /////////////////////////////////////////////////////////
   var red = GH.generateChannelHistogram(
      GH.publics.ctx.getImageData(0, 0, GH.publics.canvas.width, GH.publics.canvas.height).data, 'r'
      );
         
   $.post('testXHRPost', {channel: 'red', img: GH.publics.img.src, data: red}, function(res){
      res = jQuery.parseJSON(res);
      
      if(res.success)
         dataSaved++;
      else
         alert('An error occured while indexing an image.');
      
      isFinished(dataSaved);
   });
   
   
   // SAVE GREEN CHANNEL ///////////////////////////////////////////////////////
   var red = GH.generateChannelHistogram(
      GH.publics.ctx.getImageData(0, 0, GH.publics.canvas.width, GH.publics.canvas.height).data, 'g'
      );
         
   $.post('testXHRPost', {channel: 'green', img: GH.publics.img.src, data: red}, function(res){
      res = jQuery.parseJSON(res);
      
      if(res.success)
         dataSaved++;
      else
         alert('An error occured while indexing an image.');
      
      isFinished(dataSaved);
   });
   
   
   // SAVE BLUE CHANNEL ////////////////////////////////////////////////////////
   var red = GH.generateChannelHistogram(
      GH.publics.ctx.getImageData(0, 0, GH.publics.canvas.width, GH.publics.canvas.height).data, 'b'
      );
         
   $.post('testXHRPost', {channel: 'blue', img: GH.publics.img.src, data: red}, function(res){
      res = jQuery.parseJSON(res);
      
      if(res.success)
         dataSaved++;
      else
         alert('An error occured while indexing an image.');
      
      isFinished(dataSaved);
   });
}


/******************************************************************************
 * GENERATE CHANNEL HISTO
 * Creates a histogram for a given RGB channel based on a CanvasPixelArray
 ******************************************************************************/
GH.generateChannelHistogram = function(pPoints, pChannel, pBuckets, pMinVal, pMaxVal)
{
   if(!(pPoints instanceof Array) && !(pPoints instanceof Object))
      throw Error('Points must be passed in an array');
   
   var channel = pChannel || 'r';
   var min = pMinVal || 0;
   var max = pMaxVal || 255;
   var buckets = pBuckets || 23;
   var bucketWidth = parseInt(max / buckets);
   
   // Normalizing a histogram requires that the sum of all occurences be calculated.
   // In the case of image histograms, this number can be greater than can be
   // accomodated inside a Javascript variable.
   // 
   // To work around this limitation, we will do the following:
   //    1. Divide every occurence by a number (sizeReducer)
   //    2. Sum all the reduced occurences (reducedSum)
   //    3. Divide every original occurence by the reducedSum and sizeReducer.
   //       This will result in a normalized histogram.
   var sizeReducer = 10000;
   var reducedSum = 0;


   var histogram = [];

   // Initialize the empty histogram
   for(var i = 0; i < buckets; i++)
      histogram[i] = 0;
   
   // Resolve what part of the pixel array will be used. The format for every
   // 4 pixels is: red, green, blue, alpha (channels). So if we're only interested
   // in the green channel, for example, we'll only care about pPoints[i + 1] as
   // we loop through the array
   switch(channel)
   {
      case 'r': // i + 0
         channel = 0;
         break;
      case 'g': // i + 1
         channel = 1;
         break;
      case 'b': // i + 2
         channel = 2;
         break;
   }

   // Generate the original histogram
   for(var i = 0, len = pPoints.length; i < len; i += 4)
   {
      var index = parseInt(pPoints[i + channel] / bucketWidth);
      
      if(index == buckets)
         index = buckets - 1;
      
      histogram[index] += 1;
   }

   
   // Create the reduced histogram and sum
   for(var i = 0; i < buckets; i++)
      reducedSum += histogram[i] / sizeReducer;
   

   // Normalize the original histogram
   for(var i = 0; i < buckets; i++)
      histogram[i] = histogram[i] / reducedSum / sizeReducer;
   
   return histogram;
};


/******************************************************************************
 * GENERATE HISTO
 * Creates a histogram out of an array of frequencies
 ******************************************************************************/
GH.generateHistogram = function(pPoints, pBuckets, pMinVal, pMaxVal)
{
   if(!(pPoints instanceof Array) && !(pPoints instanceof Object))
      throw Error('Points must be passed in an array');
   
   var min = pMinVal || 0;
   var max = pMaxVal || 255;
   var buckets = pBuckets || 23;
   var bucketWidth = parseInt(max / buckets);


   var histogram = [];

   for(var i = 0; i < buckets; i++)
      histogram[i] = 0;

   for(var i = 0, len = pPoints.length; i < len; i++)
   {
      var index = parseInt(pPoints[i] / bucketWidth);
      
      if(index == buckets)
         index = buckets - 1;
      
      histogram[index] += 1;
   }

   return histogram;
};


/******************************************************************************
 * MATCH IT!
 * A test function right now that calls the server with the histogram of the
 * image in the default canvas context, and received a list of all images,
 * sorted by how close it matches the query image.
 ******************************************************************************/
 GH.matchit = function()
 {
    var red = GH.generateChannelHistogram(
      GH.publics.ctx.getImageData(0, 0, GH.publics.canvas.width, GH.publics.canvas.height).data, 'r'
      );
         
    var green = GH.generateChannelHistogram(
      GH.publics.ctx.getImageData(0, 0, GH.publics.canvas.width, GH.publics.canvas.height).data, 'g'
      );
         
    var blue = GH.generateChannelHistogram(
      GH.publics.ctx.getImageData(0, 0, GH.publics.canvas.width, GH.publics.canvas.height).data, 'b'
      );
         
    var data = {
       img: GH.publics.img.src, 
       red_data: red,
       green_data: green,
       blue_data: blue
    };
         
    $.post('matchit', data, function(res){
       
      res = jQuery.parseJSON(res);
      
      if(res.success)
      {
         $('#matches').html(res.resultHTML);
         $('#matches').show();
      }
      else
      {
         alert('An error occured while matching query image.');
      }
      
   });
         
 };

/******************************************************************************
 * UTIL SUM ARRAY
 * Adds all the values in an array
 ******************************************************************************/
GH.util.sumArray = function(pArray)
{
   if(!(pArray instanceof Array))
      throw Error('Input parameter must be passed an array');
   
   var sum = 0;
   
   for(var i = 0, len = pArray.length; i < len; i++)
      sum += pArray[i];
   
   return sum;
}



GH.testDiv = function(pVar1, pVar2)
{
   return pVar1 / pVar2;
};